local slk            = require 'jass.slk'
local dbg            = require 'jass.debug'
local jass           = require 'jass.common'
local japi           = require 'jass.japi'
local runtime        = require 'jass.runtime'
local skill          = require 'types.skill'
local handlelib      = require 'util.handle'
local flagcov        = require 'util.flag'
local symbols        = require 'symbol.item'
local sync_manager   = handle_manager_class:create()
local handle_manager = handle_manager_class:create()

local item           = {
    __tostring = function(self)
        return ('item[%s]'):format(self.name)
    end,
    __call = function(self, data)
        for k, v in pairs(data) do
            self[k] = v
        end
        return self
    end,
}
setmetatable(item, item)

--[回收价格]
item.recovery = 0.5

item.symbols = symbols

--[存放所有的物品]
item.allitems = {}

item.itemsmap = {}

--[目标类型]
item.target_flag = skill.target_flag

--[目标允许]
item.allow_flag = skill.allow_flag

item.dbgcount = 0

local mt = {}
setmetatable(mt, ac.widget)
item.__index = mt

--[物品结构]
mt.type = 'item'

--[物品类型]
-- permanent    永久    普通的不能在普通的物品
-- powerup      神符    拾取后自动使用并消失且可以满格拾取
-- charged      可充    充能层数为0后消失,价格每个充能独立计算
-- campaign     材料    独立计算价格,价格每个充能独立计算,且无论如何不能主动使用
mt.item_type = "permanent"

--[句柄]
mt.handle = jass.ConvertUnitState(0)

--[图标]
mt.art = "ReplaceableTextures\\CommandButtons\\BTNClawsOfAttack.blp"

--[模型]
mt.model = "Objects\\InventoryItems\\TreasureChest\\treasurechest.mdx"

--[物品等级]
mt.level = 1

--[提示]
mt.title = nil

--[描述]
mt.tip = ""

--[脚注]
mt.description = ""

--[主动使用]
mt.used_flag = true

--[可以丢弃]
mt.drop_flag = true

--[可以抵押]
mt.pawn_flag = true

--[是否无敌]
mt.avul_flag = false

--[可见状态]
mt.show_flag = true

--[可被选取]
mt.enum_flag = true

--[无视闭锁]
mt.force_lock = false

--[充能层数]
mt.charges = 0

--[售价]
mt.goldcost = 0

--[木价]
mt.lumbercost = 0

--[缩放]
mt.scale = 1.0

--[颜色Hex]
mt.color = 0xffffffff

--[冷却]
mt.cool = 0.0

--[魔法消耗]
mt.cost = 0

--[释放范围]
mt.range = 0.0

--[作用范围]
mt.area = 0.0

--[默认生命]
mt.life = 5

--[目标允许]
mt.allowtype = '地面'

--[目标]
mt.targettype = '没有'

--[选择圈]
mt.is_selector = false

--[冷却缩减组]
mt.cool_name = nil

--[冷却间隔生效]
mt.cool_flag = true

--[唯一性]
mt.once_flag = false

--[不打断指令]
mt.resume_flag = false

--[瞬发指令]
mt.instant_flag = false

--[自动排泄]
mt.auto_remove = true

--[自动合并] charged类道具有效,拾取后自动叠加
mt.auto_merge = false

-- 物品拾取标志 0 不处理 1 同种类唯一 2 同种类唯一 而已 该物品类型也唯一
mt.pickup_flag = 0

-- 合成优先级
mt.compound_level = 1

-- 物品种类名称
mt.race = nil

mt.enchant_message = nil 

mt.used_message = "|cffffcc00左键点击来使用|r"

mt.pawn_message = "|cffc0c0c0将物品扔在商店上以卖出|r"

-- 描述格式化数值颜色
mt.gloss = nil

mt.level = 1
mt.max_level = 1

function mt:atts_tip()
    if atts_tip then
        return atts_tip(self)
    end
    return ''
end

--[获取ID]
function mt:get_id()
    return self.name
end

--[可丢弃]
function mt:can_drop()
    return self.drop_flag
end

--[设置是否可丢弃]
function mt:set_drop(flag)
    self.drop_flag = flag
    jass.SetItemDroppable(self.handle, flag)
end

--[可使用]
function mt:can_use()
    return self.used_flag
end

--[是无敌]
function mt:is_avul()
    return self.avul_flag
end

--[设置无敌]
function mt:set_avul(flag)
    self.avul_flag = flag
    jass.SetItemInvulnerable(self.handle, flag)
end

--[可抵押]
function mt:can_pawn()
    return self.pawn_flag
end

--[设置是否可抵押]
function mt:set_pawn(flag)
    self.pawn_flag = flag
    jass.SetItemPawnable(self.handle, flag)
end

--[可见]
function mt:is_visible()
    return jass.IsItemVisible(self.handle)
end

--[显示]
function mt:show()
    self.show_flag = true
    jass.SetItemVisible(self.handle, true)
end

--[隐藏]
function mt:hide()
    self.show_flag = false
    jass.SetItemVisible(self.handle, false)
end

--[执行事件]
function mt:notify(name, ...)
    local func = self[name]
    if func then
        return select(2, xpcall(func, runtime.error_handle, self, ...))
    end
end

--[获取所在的格子]
function mt:get_slotid()
    return self.slotid
end

--[判断类型]
function mt:is_class(name)
    return self.item_type == name
end

mt._rotate_x = 0
mt._rotate_y = 0
mt._rotate_z = 0

--[按坐标X轴旋转]
function mt:rotate_x(r)
    self._rotate_x = self._rotate_x + r
    japi.EXItemMatRotateX(self.handle, r)
end

--[按坐标Y轴旋转]
function mt:rotate_y(r)
    self._rotate_y = self._rotate_y + r
    japi.EXItemMatRotateY(self.handle, r)
end

--[按坐标Z轴旋转]
function mt:rotate_z(r)
    self._rotate_z = self._rotate_z + r
    japi.EXItemMatRotateZ(self.handle, r)
end

--[重置]
function mt:matreset()
    self._rotate_x = 0
    self._rotate_y = 0
    self._rotate_z = 0
    japi.EXItemMatReset(self.handle)
end

--[获取旋转值]
function mt:get_rotate()
    return self._rotate_x, self._rotate_y, self._rotate_z
end

--[整体旋转]
function mt:rotate(x, y, z)
    self:matreset()
    self:rotate_x(x or 0)
    self:rotate_y(y or 0)
    self:rotate_z(z or 0)
end

--[获取使用次数]
function mt:get_charges()
    if self._away_charges == 0 or self._away_charges == 1 then
        return self._src_charges
    end
    return self.charges
    --return jass.GetItemCharges(self.handle)
end

--[增加充能次数]
function mt:add_charges(count)
    self:set_charges(self:get_charges() + count)
end

--[设置使用次数]
function mt:set_charges(charges)
    if self._away_charges == 0 or self._away_charges == 1 then
        self._away_charges = 1
        self._src_charges = math.max(0, charges)
        return
    end
    self.charges = charges
    jass.SetItemCharges(self.handle, math.max(0, charges))
end

--[掉落物品] (强制)
function mt:drop(where)
    if self.owner then
        jass.UnitRemoveItem(self.owner.handle, self.handle)
        if where then
            self:set_point(where)
        end
    end
end

--[丢弃物品] (命令型,单位被控制了可能丢不掉)
function mt:lose(where)
    if self.owner then
        where = where or self.owner:get_point()
        local x, y = where:get()
        return jass.UnitDropItemPoint(self.owner.handle, self.handle, x, y)
    end
    return false
end

-- 丢弃物品到指定坐标
function mt:discard(where)
    where = where or self.owner:get_point()
    local x, y = where:get()
    jass.SetItemPosition(self.handle, x, y)
end

function mt:can_merge()
    return (self:is_class('charged') or self:is_class('campaign') ) and self:get_charges() > 0 and self.auto_merge
end

--[给与物品]
function mt:give(unit)
    if self._pick_ignore_flag then 
        self._pick_give_unit = unit 
        return 
    end
    if not self.owner then
        self:set_point(unit:get_point())
        if unit:dispatch("单位-检查物品合成", unit, self) then 
            unit:notify('单位-合成物品', unit, self)
            return
        end
        if self:can_merge() then
            local has = unit:find_item(self.name)
            if has then
                has:add_charges(self:get_charges())
                self:remove()
                return
            end
        end
        if jass.UnitAddItem(unit.handle, self.handle) == false then
            unit:notify('单位-给与物品失败', unit, self)
        end
    end
end

--[物品存活]
function mt:is_alive()
    if self.removed then
        return false
    end
    return jass.GetWidgetLife(self.handle) > 0
end

--[设置生命值]
function mt:set_life(life)
    if self.removed then
        return 
    end
    jass.SetWidgetLife(self.handle, math.max(life, 1))
end

--[获取生命值]
function mt:get_life()
    if self.removed then
        return 0
    end
    return jass.GetWidgetLife(self.handle)
end

function mt:update_data()
    local data = ac.lni('item',self.name) or {}
    self.data = data
    for k,v in pairs(data) do
        self[k] = format_obj(self,v,self.level)
    end
end

--[获取等级]
function mt:get_level()
    return self.level
end

--[设置等级]
function mt:set_level(level)
    self.level = level
    self:update_data()
end

function mt:add_level(ex)
    self.level = math.min(self.level + (ex or 1),self.max_level)
    self:update_data()
end

--[获取持有者]
function mt:get_owner()
    return self.owner
end

--[获取模型]
function mt:get_model()
    return self.model
end

--[设置模型]
function mt:set_model(model)
    self.model = model
    if self.handle ~= 0 then
        japi.SetUnitModel(self.handle, self.model)
    end
end

--[获取模型缩放]
function mt:get_scale()
    return self.scale
end

--[设置模型缩放]
function mt:set_scale(scale)
    self.scale = scale
    if self.handle ~= 0 then
        japi.EXSetItemSize(self.handle, scale)
    end
end

--[获取颜色]
function mt:get_color()
    return self.color
end

--[设置模型颜色]
function mt:set_color(color)
    self.color = color
    if self.handle ~= 0 then
        japi.EXSetItemColor(self.handle, color)
    end
end

--[获取技能图标]
function mt:get_art()
    return self.art
end

--[获取暗图标路径]
function mt:get_dis_art()
    if not self.disabled_art then
        self.disabled_art = japi.GetDisabledIcon(self.art)
    end
    return self.disabled_art
end

--[设置图标]
function mt:set_art(art, refresh)
    if self.once_flag then
        self.art = art
        japi.EXSetItemDataString(FourCC(self._id), 0x01, art)
        if refresh then
            self:refresh()
        end
    end
end

--[获取名称]
function mt:get_title()
    return self.title or self.name
end

--[设置名称]
function mt:set_title(title)
    if self.once_flag then
        self.title = title
        japi.EXSetItemDataString(FourCC(self._id), 0x04, format_obj(self, title))
    end
end

--[获取描述]
function mt:get_tip()
    if rawget(self, "tip") ~= "nil" then 
        local tip = self.data.tip or self.tip
        return format_obj(self, tip)
    end 
    return self.tip
end

--[基础描述]
function mt:formatter_common_tip()
    return ""
end

--[属性描述]
function mt:formatter_states_tip()
    --return ac.affix(self)
    return ''
end

--[附加描述]
function mt:formatter_attach_tip()
    if rawget(self, "tip") ~= "nil" then 
        local tip = self.data.tip or self.tip
        return format_obj(self, tip)
    end 
    return self.tip 
end

--[生成描述]
function mt:formatter_tip()
    -- local common = self:formatter_common_tip()
    local states = self:formatter_states_tip()
    local common = self:formatter_attach_tip()
    local attach = self.attachtip
    local buffer = { }
    buffer[#buffer+1] = states
    buffer[#buffer+1] = common
    if attach then 
        if #common > 0 then 
            attach = "|n|n" .. attach 
        end 
        buffer[#buffer+1] = attach
    end 
    return table.concat(buffer)
end

--[设置描述]
function mt:set_tip(tip)
    self.tip = tip
    self.tip_ui = self:formatter_tip()
end

--[刷新描述]
function mt:refresh_tip()
    self.tip_ui = self:formatter_tip()
end

--[获取简介]
function mt:get_description()
    return self.description
end

--[设置简介]
function mt:set_description(description)
    self.description = description
    if self.once_flag then
        japi.EXSetItemDataString(FourCC(self._id), 0x05, description)
    end
end

--[获取位置]
function mt:get_point()
    return ac.point(jass.GetItemX(self.handle), jass.GetItemY(self.handle))
end

--[设置坐标]
function mt:set_point(point)
    local x, y = point:get()
    if not self.owner then
        jass.SetItemPosition(self.handle, x, y)
    end
end

--[获取消耗资源]
function mt:get_resource()
    local gold = self.goldcost
    local wood = self.lumbercost
    local charges = math.max(self.charges, 1)
    if self:is_class('campaign') then
        gold = gold * charges
        wood = wood * charges
    elseif self:is_class('charged') then
        gold = gold / charges * self:get_charges()
        wood = wood / charges * self:get_charges()
    end
    gold = math.modf(gold * item.recovery)
    wood = math.modf(wood * item.recovery)
    return math.floor(gold), math.floor(wood)
end

--[修改目标类型]
function mt:set_targettype(data)
    if self.once_flag then
        self.targettype = data
        item.set_abilitydata(self, 0x6D, flagcov.tobit(self.targettype, item.target_flag), true)
    end
end

--[修改目标允许]
function mt:set_allow(data)
    if self.once_flag then
        self.allowtype = data
        item.set_abilitydata(self, 0x64, flagcov.tobit(self.allowtype, item.allow_flag))
    end
end

--[获取冷却时间]
function mt:get_cool()
    return self.cool
end

--[设置冷却时间]
function mt:set_cool(cool)
    if self.once_flag then
        self.cool = cool
        item.set_abilitydata(self, 0x69, cool, true)
    end
end

--[获取作用范围]
function mt:get_area()
    return self.area
end

--[设置作用范围]
function mt:set_area(area)
    if self.once_flag then
        self.area = area
        item.set_abilitydata(self, 0x6A, area, true)
    end
end

--[获取法力消耗]
function mt:get_cost()
    return self.cost
end

--[设置法力消耗]
function mt:set_cost(cost)
    if self.once_flag then
        self.cost = cost
        item.set_abilitydata(self, 0x68, cost)
    end
end

--[获取施法范围]
function mt:get_range()
    return self.range
end

--[设置施法范围]
function mt:set_range(range)
    if self.once_flag then
        self.range = range
        item.set_abilitydata(self, 0x6B, range, true)
    end
end

--[处于冷却]
function mt:is_cooling()
    return self:get_cooling() > 0
end

--[获取冷却间隔组]
function mt:get_cool_name()
    if not self.cool_name then
        return self:get_id()
    end
    return self.cool_name
end

--[获取剩余冷却]
function mt:get_cooling()
    local unit = self.owner
    if unit and unit.item_cool_data and self.cool_flag then
        local name = self:get_cool_name()
        local data = unit.item_cool_data[name]
        if data then
            return math.max(data._remaing - (ac.clock() / 1000 - data._cooling), 0)
        else
            return 0
        end
    end
    if not self._cooling or not self._remaing then
        return 0
    end
    return math.max(self._remaing - (ac.clock() / 1000 - self._cooling), 0)
end

--[设置冷却]
function mt:set_cooling(cool)
    local unit = self.owner
    local cool_name = self:get_cool_name()
    local current_cool = math.min(self.cool, cool or self.cool)
    self._cooling = ac.clock() / 1000
    self._remaing = current_cool
    if unit and self.cool_flag then
        unit.item_cool_data[cool_name] = {
            _cooling = self._cooling,
            _remaing = self._remaing,
        }
    end
    if self._away_cooling == 0 or self._away_cooling == 1 then
        self._away_cooling = 1
        return
    end
    if not self.removed then
        japi.EXSetAbilityState(self:get_ability(), 0x01, current_cool)
    end
    if unit and self.cool_flag then
        -- 如果有CD间隔组
        for i = 1, 6 do
            local slot = unit.items[i]
            if slot and slot ~= self and slot:get_cool_name() == cool_name and slot.cool_flag then
                japi.EXSetAbilityState(slot:get_ability(), 0x01, current_cool)
            end
        end
    end
end

--[进入冷却]
function mt:cooling()
    self:set_cooling(self:get_cool())
end

--[摧毁物品]
function mt:destroy()
    if self.removed then return end
    if not self:is_alive() then return end
    if self.owner then
        self._wait_destroy = true
        if self._wait_manipulated then
            return
        end
        self:drop()
        pcall(japi.SetUnitModel, self.handle, "null.mdx")
    end
    --- 切记切记!傻逼war3摧毁一个隐藏的物品将会导致handle直接被回收
    jass.SetItemInvulnerable(self.handle, false)
    jass.SetItemVisible(self.handle, true)
    -- jass.SetWidgetLife(self.handle, 0)
    pcall(jass.UnitDamageTarget, ac.dummy_unit.handle, self.handle, 99999999.0, false, false, 1,4, 0)
end

--[删除物品]
function mt:remove()
    if self.owner then
        -- ('被持有中,等待删除')
        self._wait_remove = true
        if self._wait_manipulated then
            return
        end
        self:drop()
    end

    if self:is_alive() then
        self:destroy()
    end

    if self.removed then return end
    self.removed = true

    ac.game:notify('物品-删除', self)
	self:gc_clear()

    self:flush_sync_id()

    local handle = self.handle

    pcall(jass.SetWidgetLife, handle, 100)
    jass.RemoveItem(handle)
    dbg.handle_unref(handle)

    item.allitems[handle] = nil

    if self.once_flag and not self.native_flag then
        item.free(self.item_type, self.id)
    end

    item.dbgcount = item.dbgcount - 1

    self.handle = nil
end

--[判断释放条件]
function mt:use_check()
    local u = self.owner
    if not u then
        return false
    end
    if self.removed then
        return false
    end
    if not self:is_alive() then
        return false
    end
    if not self:can_use() then
        return false
    end
    if self:get_cooling() > 0 then
        return false
    end
    if (u:get('魔法') < self:get_cost()) then
        return false
    end
    if (u:has_restriction '晕眩') and not self.physics then
        return false
    end
    if u:is_dead() then
        return false
    end
    if u:is_hidden() then
        return false
    end
    if u:is_loaded() then
        return false
    end
    return true
end

--[使用物品]
function mt:use(target)
    local unit = self.owner
    if unit and self:use_check() then
        local bool = true
        if not target then
            bool = jass.UnitUseItem(unit.handle, self.handle)
        elseif not target.handle then
            local x, y = target:get()
            bool = jass.UnitUseItemPoint(unit.handle, self.handle, x, y)
        else
            bool = jass.UnitUseItemTarget(unit.handle, self.handle, target.handle)
        end
        return bool
    end
    return false
end

--[刷新]
function mt:refresh()
    if self.removed then
        return
    end

    if not self.owner then
        return
    end

    local unit = self.owner
    -- 给物品占位置
    local size = unit:get_inventory_size()
    local cool = self:get_cooling()

    if not item.fills then
        local fills = {}
        local point = ac.point(0, 0)
        for i = 1, 6 do
            local fill = item.create("填充器")
            fill:hide()
            fills[i] = fill
        end
        item.fills = fills
    end

    for i = 1, size do
        item.fills[i]:give(unit)
    end

    ac.ignore_flag = true
    jass.UnitRemoveItem(unit.handle, self.handle)
    jass.UnitAddItem(unit.handle, self.handle)
    ac.ignore_flag = false

    for i = 1, size do
        item.fills[i]:drop()
        item.fills[i]:hide()
    end

    self:set_cooling(cool)
end

-- 本地使用物品 只能放无目标技能
function mt:client_use()
    if self.targettype == '没有' and self:use_check() and self.syncid then
        local h, t = (('%04d'):format(self.syncid)):match("(%d%d)(%d%d)")
        ac.message.sync("客户端-使用物品", tonumber(h), tonumber(t), ac.message.convert_flag("独立|交互|恢复"))
        return true
    end
    return false
end

function mt:register_sync_id()
    if not self.syncid then 
        local syncid = sync_manager:allocate()
        self.syncid = syncid
        item.itemsmap[syncid] = self
    end 
end

function mt:flush_sync_id() 
    if self.syncid then 
        local syncid = self.syncid
        item.itemsmap[syncid] = nil
    end 
end 

--[获得技能句柄(jass)]
function mt:get_ability(slotid)
    if self.removed then
        return jass.ConvertUnitState(0)
    end
    return japi.GetItemAbility(self.handle, slotid or 0)
end

item.tooltips = {

    hotkeys = { '7', '8', '4', '5', '1', '2', },

    art = function(self)
        return self:get_art()
    end,

    title = function(self)
        local title = format_obj(self, self:get_title())
        if self.owner and self:can_use() and self.slotid then
            return title .. (' (|cffffcc00小键盘%s|r)'):format(item.tooltips.hotkeys[self.slotid])
        end
        return title
    end,

    tip = function(self)
        self:refresh_tip()
        return self.tip_ui
    end,

    requires = function(self)
        return self.requires
    end,

    usedtip = function(self)
        if self:can_use() then
            return self.used_message
        end
    end,
    soldtip = function(self)
        if self:can_pawn() then
            return self.pawn_message
        end
    end,
    enchant = function(self)
        return self.enchant_message
    end,
    message = function(self)
        -- local buf 
        -- if self.used_flag then 
        --     buf = buf or { }
        --     buf[#buf+1] = "|cff64ff64主动使用|r"
        -- end 
        -- if not self.drop_flag then 
        --     buf = buf or { }
        --     buf[#buf+1] = "|cffff6464不可丢弃|r"
        -- end 
        -- if buf then 
        --     return table.concat(buf, "|r|n")
        -- end 
        return self.messagetip 
    end,
    resource = function(self)
        local gold, wood = self:get_resource()
        return {
            cool = self:get_cool(),
            cost = self:get_cost(),
            gold = gold,
            wood = wood,
        }
    end,

}

-- 获取UI信息
function mt:get_tooltips(name)
    if name and item.tooltips[name] then
        return item.tooltips[name](self)
    end
end

function mt:gc(target)
	--目前只支持两种自动回收
	--1为function，即属性
	--2为带有remove方法的对象，如特效，计时器，触发
	if not self._gc_target then
		self._gc_target = {}
	end
	table.insert(self._gc_target,target)
end

function mt:gc_clear()
	if self._gc_target then
		for k,v in ipairs(self._gc_target) do
			--function针对属性，调用后还原
			if type(v) == 'function' then
				v()
			else
				--remove针对计时器、触发、特效等，调用后删除
				v:remove()
			end
		end
		self._gc_target = nil
	end
end

--[初始化]
function item.init_item(self)
    japi.SetUnitModel(self.handle, self.model)
    if japi.EXSetItemSize then 
        japi.EXSetItemSize(self.handle, self.scale)
    end 
    if japi.EXSetItemColor then 
        japi.EXSetItemColor(self.handle, self.color)
    end 
    jass.SetItemDroppable(self.handle, self.drop_flag)
    jass.SetItemPawnable(self.handle, self.pawn_flag)
    jass.SetItemInvulnerable(self.handle, self.avul_flag)
    jass.SetItemVisible(self.handle, self.show_flag)
    jass.SetWidgetLife(self.handle, self.life)
    if self:is_class('charged') then
        self.charges = math.max(self.charges, 1)
    end

    jass.SetItemCharges(self.handle, self.charges)

    if self:is_class('powerup') or self:is_class('campaign') then
        self.used_flag = false
    end
    self:set_level(self.level)
    self:set_title(self.title)
    self:set_tip(self.tip)

    if self.initialized then
        return self
    end

    if not self.once_flag or self.native_flag then
        getmetatable(self).__index.initialized = true
    end

    local itemid = FourCC(self._id)

    japi.EXSetItemDataString(itemid, 0x01, self.art)
    japi.EXSetItemDataString(itemid, 0x04, self.name)
    japi.EXSetItemDataString(itemid, 0x03, self.tip_ui)
    japi.EXSetItemDataString(itemid, 0x05, self.description)
    -- japi.EXSetItemDataString(itemid, 0x05, self.tip_ui)

    if self.native_flag then
        return self
    end

    local abilList = slk.item[self._id]['abilList']
    self.abilityid = abilList and abilList:sub(1, 4) or 'rat9'

    local abilityid = FourCC(self.abilityid)
    jass.UnitAddAbility(ac.dummy_unit.handle, abilityid)
    jass.SetUnitAbilityLevel(ac.dummy_unit.handle, abilityid, 2)

    local handle = japi.EXGetUnitAbility(ac.dummy_unit.handle, abilityid)

    jass.SetUnitAbilityLevel(ac.dummy_unit.handle, abilityid, 2)

    japi.EXSetAbilityDataReal(handle, 1, 0x69, self.cool)
    japi.EXSetAbilityDataReal(handle, 1, 0x6A, self.area)
    japi.EXSetAbilityDataReal(handle, 1, 0x6B, self.range)
    japi.EXSetAbilityDataReal(handle, 1, 0x6C, 0.0)   -- 这里必须是 0 否则 插旗子就会出现bug
    japi.EXSetAbilityDataInteger(handle, 1, 0x68, self.cost)
    japi.EXSetAbilityDataInteger(handle, 1, 0xC8, 0x201)
    japi.EXSetAbilityDataReal(handle, 1, 0x6E, 1)
    japi.EXSetAbilityDataReal(handle, 1, 0x6D, 0)
    japi.EXSetAbilityDataReal(handle, 1, 0x70, 0)
    
    if self:can_use() then
        japi.EXSetAbilityDataInteger(handle, 1, 0x64, flagcov.tobit(self.allowtype, item.allow_flag))
        japi.EXSetAbilityDataReal(handle, 1, 0x6D, flagcov.tobit(self.targettype, item.target_flag))
        japi.EXSetAbilityDataReal(handle, 1, 0x6E, self.is_selector and 3 or 1)
    end

    jass.SetUnitAbilityLevel(ac.dummy_unit.handle, abilityid, 1)
    jass.UnitRemoveAbility(ac.dummy_unit.handle, abilityid)

    return self
end

function item.allocate(className)
    local itempool = item.symbols[className]
    if itempool then
        local top = #itempool
        local res = itempool[top]
        itempool[top] = nil
        return res
    end
end

function item.free(className, itemid)
    local itempool = item.symbols[className]
    if itempool then
        itempool[#itempool + 1] = itemid
    end
end

function item.init()
    local trigger = {}
    item.trigger = trigger
    trigger.action = function()
        local triggeritem = item.j_item(jass.GetTriggerWidget())
        if triggeritem then
            triggeritem.destroy_clock = ac.clock() / 1000
            ac.game:notify('物品-被破坏', triggeritem)
        end
    end
    trigger.handle = ac.event.trigger()
    jass.TriggerAddAction(trigger.handle, trigger.action)
    ac.loop(250, function()
        item.update()
    end)
end

function item.update()
    local clock = ac.clock() / 1000
    for handle, object in pairs(item.allitems) do
        if object.auto_remove and not object:is_alive() then
            if not object.destroy_clock then
                object:remove()
            else
                if (clock - object.destroy_clock) > 3.0 then
                    object:remove()
                end
            end
        end
    end
end

function item.j_item(handle)
    return item.allitems[handle]
end

--[修改技能数据]
function item.set_abilitydata(obj, data_id, value, isreal)
    if obj.initialized or not obj.abilityid then
        return false
    end
    local abilityid = FourCC(obj.abilityid)
    jass.UnitAddAbility(ac.dummy_unit.handle, abilityid)
    jass.SetUnitAbilityLevel(ac.dummy_unit.handle, abilityid, 2)
    local handle = japi.EXGetUnitAbility(ac.dummy_unit.handle, abilityid)
    if isreal then
        japi.EXSetAbilityDataReal(handle, 1, data_id, value)
    else
        japi.EXSetAbilityDataInteger(handle, 1, data_id, value)
    end
    jass.SetUnitAbilityLevel(ac.dummy_unit.handle, abilityid, 1)
    jass.UnitRemoveAbility(ac.dummy_unit.handle, abilityid)
end

ac.message.sync:on "客户端-使用物品" (function(_, player, unit, head, tail)
    local syncid = (head) * 100 + (tail)
    local manipulated = item.itemsmap[syncid]
    if manipulated and manipulated.targettype == "没有" and manipulated:use_check() then
        local owner = manipulated.owner
        if owner == unit then
            unit:set('魔法',unit:get('魔法') - manipulated:get_cost())
            local charges = manipulated:get_charges()
            local cooldown = manipulated:get_cool()
            manipulated:set_cooling(cooldown)
            manipulated._away_cooling = 0
            manipulated._away_charges = 0
            manipulated._src_charges = math.max(0, charges)
            unit:notify('单位-使用物品', unit, manipulated)
            local away_cooling = manipulated._away_cooling
            local away_charges = manipulated._away_charges
            if away_charges == 1 then
                charges = manipulated:get_charges()
            else
                charges = charges - 1
            end
            manipulated._away_cooling = -1
            manipulated._away_charges = -1
            if away_cooling == 1 then
                cooldown = manipulated:get_cooling()
            end
            manipulated:set_cooling(cooldown)
            if not manipulated.removed then
                manipulated:set_charges(charges)
                if manipulated:is_class("charged") and manipulated:get_charges() == 0 then
                    manipulated:destroy()
                end
            end
            unit:notify('单位-刷新物品显示', unit)
            unit._last_used_item = nil
        end
    end
end)

ac.game:on "单位-本地命令" (function(t, unit, name, order, flags, ...)
    if unit.items then
        local orderid = OrderId[order]
        if orderid and orderid >= 0xD0028 and orderid <= 0xD002D then
            local slotid = orderid - 0xD0027
            local manipulated = unit.items[slotid]
            if manipulated then
                -- 连续an两次物品 不让他放
                if name == "点目标命令" then
                    local x1, y1 = ...
                    local x2, y2 = jass.GetItemX(manipulated.handle), jass.GetItemY(manipulated.handle)
                    x1 = math.ceil(x1 * 100)
                    y1 = math.ceil(y1 * 100)
                    x2 = math.ceil(x2 * 100)
                    y2 = math.ceil(y2 * 100)
                    if x1 == x2 and y1 == y2 then
                        return false
                    end
                end
                if not manipulated:can_use() then
                    return false
                end
                if unit:dispatch("单位-尝试使用物品", unit, manipulated) == false then
                    return false
                end
                if manipulated:notify("on_order_hook", flags, ...) == false then
                    return false
                end
                if manipulated.instant_flag and not flags:find("瞬发") then 
                    manipulated:client_use()
                    return false
                elseif manipulated.resume_flag and not flags:find("恢复") then
                    local handle = manipulated.handle
                    local flag = ac.message.convert_flag(flags .. "|恢复")
                    if name == "无目标命令" then
                        ac.message:order_call({ "order_immediate", orderid, handle, flag, })
                    elseif name == "点目标命令" then
                        local x, y = ...
                        ac.message:order_call({ "order_point", orderid, handle, x, y, flag, })
                    elseif name == "指定目标命令" then
                        local widget, x, y = ...
                        ac.message:order_call({ "order_target", orderid, handle, x, y, widget, flag, })
                    end
                    return false
                end
            end
        end
    end
end)


local item_cast_targets = {
    ['单位'] = function()
        return ac.unit.j_unit(jass.GetOrderTargetUnit())
    end,
    ['物品'] = function()
        return ac.item.j_item(jass.GetOrderTargetItem())
    end,
    ['可破坏物'] = function()
        return ac.destructable.j_destructable(jass.GetOrderTargetDestructable())
    end,
    ['点'] = function()
        return ac.point(jass.GetOrderPointX(), jass.GetOrderPointY())
    end,
}

-- 寻找技能释放目标
function item.get_cast_target(name)
    if name and item_cast_targets[name] then
        return item_cast_targets[name]()
    end
end

local auto_targets = {
    ['物体'] = { '单位', '物品', '可破坏物', },
    ['点'] = { '点', },
    ['点&物体'] = { '单位', '物品', '可破坏物', '点', },
}

-- 检索技能目标
local function auto_search_target(manipulated)
    if manipulated:can_use() and auto_targets[manipulated.targettype] then
        local target
        local search = auto_targets[manipulated.targettype]
        if search then 
            for i = 1,#search do 
                target = item.get_cast_target(search[i]); if target then break end;
            end 
            if not target then 
                log.error("发生错误，没有找到相关的目标", manipulated, manipulated.targettype)
            end 
            return target
        end 
    end
end

ac.game:on "单位-发布命令" (function (_, unit, orderid)
    if ac.ignore_flag then return end
    local unit = ac.unit(jass.GetTriggerUnit())
    if not unit or unit.removed then return end
    if orderid >= 0xD0028 and orderid <= 0xD002D then
        local slotid = orderid - 0xD0027
        local manipulated = unit.items[slotid]
        if manipulated and manipulated:can_use() then
            manipulated._last_used_target = auto_search_target(manipulated)
        end
    end
end )

ac.event.register("任意单位-发动技能效果", function()
    if ac.ignore_flag then return end
    local unit = ac.unit(jass.GetTriggerUnit())
    if not unit or unit.removed then return end
    local orderid = jass.GetUnitCurrentOrder(unit.handle)
    local manipulated
    if orderid >= 0xD0028 and orderid <= 0xD002D then
        local slotid = orderid - 0xD0027
        manipulated = unit.items[slotid]
    end
    if not manipulated or not manipulated:can_use() then
        return
    end
    unit._last_used_item = manipulated
    local charges = manipulated:get_charges()
    local cooldown = manipulated:get_cool()
    manipulated:set_cooling(cooldown)
    manipulated._away_cooling = 0
    manipulated._away_charges = 0
    manipulated._src_charges = math.max(0, charges)
    unit:notify('单位-使用物品', unit, manipulated, manipulated._last_used_target)
    local away_cooling = manipulated._away_cooling
    local away_charges = manipulated._away_charges
    -- 在使用物品期间 修改过物品充能层数
    if away_charges == 1 then
        charges = manipulated:get_charges()
    else
        charges = charges - 1
    end
    manipulated._away_cooling = -1
    manipulated._away_charges = -1
    -- 在 使用物品期间 设置过CD 所以CD 等于最后设置的CD才是正确的
    if away_cooling == 1 then
        cooldown = manipulated:get_cooling()
    end
    unit:wait(0, function()
        manipulated:set_cooling(cooldown)
        if not manipulated.removed then
            manipulated:set_charges(charges)
            if manipulated:is_class("charged") and manipulated:get_charges() == 0 then
                manipulated:destroy()
            end
        end
        unit:notify('单位-刷新物品显示', unit)
    end)
    manipulated._last_used_target = nil
    unit._last_used_item = nil
end)

ac.event.register("任意单位-获得物品", function()
    if ac.ignore_flag then return end
    local unit = ac.unit(jass.GetTriggerUnit())
    if not unit or unit.removed then return end
    local manipulated = item.j_item(jass.GetManipulatedItem())
    if not unit or not manipulated then
        return
    end
    manipulated._pick_ignore_flag = true
    local ok, res = unit:dispatch("单位-即将获得物品", unit, manipulated)
    if ok == false then
        if res then
            unit:get_owner():message(res)
        end
        ac.ignore_flag = true
        jass.UnitRemoveItem(unit.handle, manipulated.handle)
        ac.ignore_flag = false
        manipulated._pick_ignore_flag = false
        local new_picker = manipulated._pick_give_unit
        manipulated._pick_give_unit = nil 
        if new_picker then 
            manipulated:give(new_picker)
        end 
        return
    end
    manipulated._pick_give_unit = nil
    manipulated._pick_ignore_flag = false
    if manipulated:is_class("powerup") then
        unit:notify("单位-获得神符", unit, manipulated)
        return
    end
    if not unit.item_cool_data then
        unit.item_cool_data = {}
    end
    if manipulated.cool_flag == true then
        local name = manipulated:get_cool_name()
        local data = unit.item_cool_data[name]
        if data then
            local coolnode = data._cooling + data._remaing
            local newcool = coolnode - (ac.clock() / 1000)
            local maxcool = manipulated:get_cool()
            if newcool > 0 then
                japi.EXSetAbilityDataReal(manipulated:get_ability(), 1, 0x69, newcool)
                japi.EXSetAbilityState(manipulated:get_ability(), 0x01, newcool)
                japi.EXSetAbilityDataReal(manipulated:get_ability(), 1, 0x69, maxcool)
            else
                japi.EXSetAbilityState(manipulated:get_ability(), 0x01, 0)
            end
        else 
            japi.EXSetAbilityState(manipulated:get_ability(), 0x01, 0)
        end
    else
        local cooling = manipulated._cooling 
        local remaing = manipulated._remaing 
        if cooling and remaing then
            local coolnode = cooling + remaing
            local newcool = coolnode - (ac.clock() / 1000)
            local maxcool = manipulated:get_cool()
            if newcool > 0 then
                japi.EXSetAbilityDataReal(manipulated:get_ability(), 1, 0x69, newcool)
                japi.EXSetAbilityState(manipulated:get_ability(), 0x01, newcool)
                japi.EXSetAbilityDataReal(manipulated:get_ability(), 1, 0x69, maxcool)
            else
                japi.EXSetAbilityState(manipulated:get_ability(), 0x01, 0)
            end
        else 
            japi.EXSetAbilityState(manipulated:get_ability(), 0x01, 0)
        end
    end
    for index = 1, 6 do
        local handle = jass.UnitItemInSlot(unit.handle, index - 1)
        if handle == manipulated.handle then
            manipulated.slotid = index
            break
        end
    end
    manipulated.owner = unit
    unit.items[manipulated.slotid] = manipulated
    unit.inventory_count = unit.inventory_count + 1
    ac.game:notify('物品-被拾取', manipulated, unit)
    unit:notify('单位-获得物品', unit, manipulated)
    unit:notify('单位-刷新物品显示', unit)
end)

ac.event.register("任意单位-丢弃物品", function()
    if ac.ignore_flag then return end
    local unit = ac.unit(jass.GetTriggerUnit())
    if not unit or unit.removed then return end
    local manipulated = item.j_item(jass.GetManipulatedItem())
    if not unit or not manipulated then
        return
    end
    if manipulated:is_class("powerup") then
        ac.ignore_flag = true
        jass.UnitRemoveItem(unit.handle, manipulated.handle)
        ac.ignore_flag = false
        manipulated:destroy()
        return
    end
    manipulated._wait_manipulated = true
    local slotid = manipulated.slotid
    unit.inventory_count = unit.inventory_count - 1
    ac.game:notify('物品-被丢弃', manipulated, unit)
    unit:notify('单位-丢弃物品', unit, manipulated)
    unit.items[slotid] = nil
    manipulated.slotid = nil
    manipulated.owner = nil
    manipulated._wait_manipulated = false
    if manipulated._wait_destroy then
        ac.ignore_flag = true
        jass.UnitRemoveItem(unit.handle, manipulated.handle)
        ac.ignore_flag = false
        manipulated:destroy()
    elseif manipulated._wait_remove then
        ac.ignore_flag = true
        jass.UnitRemoveItem(unit.handle, manipulated.handle)
        ac.ignore_flag = false
        manipulated:remove()
    end
end)

ac.event.register("任意单位-抵押物品", function()
    if ac.ignore_flag then return end
    local unit = ac.unit.j_unit(jass.GetTriggerUnit())
    if not unit or unit.removed then return end
    local owner = unit:get_owner()
    local shop = ac.unit.j_unit(jass.GetBuyingUnit())
    local manipulated = ac.item.j_item(jass.GetSoldItem())
    if not unit or not shop or not manipulated then
        return
    end
    local goldcost, lumbercost = manipulated:get_resource()
    if goldcost > 0 then
        owner:add('金币', goldcost)
    end
    if lumbercost > 0 then
        owner:add('晶石', lumbercost)
    end
    if goldcost > 0 and lumbercost > 0 then
        unit:jump_gold_wood(goldcost, lumbercost)
    elseif goldcost > 0 then
        unit:jump_gold(goldcost)
    elseif lumbercost > 0 then
        unit:jump_wood(lumbercost)
    end
    local point = unit:get_point()
    point:add_effect('UI\\Feedback\\GoldCredit\\GoldCredit.mdx'):set_height(point:get_height()):remove()
    unit:play_sound('金币')
    manipulated._wait_manipulated = true
    unit:notify('单位-出售物品', unit, manipulated)
    manipulated:remove()
    manipulated._wait_manipulated = false
end)

-- ac.event.register("任意单位-使用物品", function()
--     if ac.ignore_flag then return end
--     local unit = ac.unit(jass.GetTriggerUnit())
--     if not unit or unit.removed then return end
--     local manipulated = item.j_item(jass.GetManipulatedItem())
--     if not unit or not manipulated or manipulated.removed then
--         return
--     end
--     print("使用物品", os.clock() )
--     if manipulated:get_charges() == 0 then
--         manipulated:set_charges(0)   -- 不设置就不能使用了
--     end
-- end)

ac.event.register("任意单位-发布指定目标命令", function()
    if ac.ignore_flag then return end
    local unit = ac.unit(jass.GetTriggerUnit())
    if not unit or unit.removed then return end
    local target = ac.item.j_item(jass.GetOrderTarget())
    if not target then return end
    local orderid = jass.GetIssuedOrderId()
    if orderid == 0xD0003 then
        local point = target:get_point()
        local ok, res = unit:dispatch("单位-发布拾取命令", unit, target)
        if ok == false then
            unit:issue_order("smart", point)
            return
        end
    elseif orderid >= 0xD0022 and orderid <= 0xD0027 then
        local slotid = orderid - 0xD0021
        local manipulated = target             -- a
        local replaceitem = unit.items[slotid] -- b
        if replaceitem and manipulated == replaceitem then
            unit:notify("单位-右键双击物品", unit, manipulated)
            manipulated:notify("on_double_clicked")
            return
        end
        unit:notify('单位-移动物品', unit, manipulated, replaceitem)
        ac.wait(0, function()
            if not unit.removed then
                for index = 1, 6 do
                    local handle = jass.UnitItemInSlot(unit.handle, index - 1)
                    local slotwp = ac.item.j_item(handle)
                    if slotwp then
                        slotwp.slotid = index
                    end
                    unit.items[index] = slotwp
                end
                unit:notify('单位-刷新物品显示', unit)
            end
        end)
    end
end)

--新建物品对象
function item.new(name,data)

    local meta = ac.item[name]

    if not meta then
        log.info("尝试创建没有注册的物品", name)
        return nil
    end

    local obj = data or {}
    setmetatable(obj, meta.prototype)

    if meta.native_flag == true then
        if not meta._id then
            log.error("原生物品必须要指定“_id”为物品的ID", name)
            return
        end
        meta.once_flag = false
    end
    obj:update_data()
    return obj
end

--[创建物品]
function item.create(name, where, data)
    local meta = ac.item[name]
    local obj = item.new(name, data)

    if not obj._id then
        obj._id = item.allocate(meta.item_type)
        if not meta.once_flag then
            meta._id = obj._id
        end
    end
    where = where or ac.point(0, 0)

    local x, y = where:get()

    local handle = jass.CreateItem(FourCC(obj._id), x, y)
    if not handle or handle == 0 then
        log.error('物品创建失败', name, obj._id)
        return
    end

    item.dbgcount = item.dbgcount + 1

    obj.handle = handle
    dbg.handle_ref(handle)
    item.allitems[handle] = obj

    jass.TriggerRegisterDeathEvent(item.trigger.handle, handle)
    item.init_item(obj)

    if obj.instant_flag then 
        obj:register_sync_id()
    end

    obj:notify("on_create")
    ac.game:notify("物品-创建", obj)

    return obj
end


--[点创建物品]
function ac.point.__index:add_item(name, data)
    return item.create(name, self, data)
end

--[单位创建物品]
function ac.unit.__index:add_item(name, data)
    local where = self:get_point()
    local obj = item.create(name, where, data)
    obj:give(self)
    return obj
end

--[单位查找物品]
function ac.unit.__index:find_item(name)
    if not name then
        return self.items
    end
    for obj in self:search_items() do
        if obj.name == name then
            return obj
        end
    end
end

--[获取单位物品]
function ac.unit.__index:get_items()
    local items = {}
    for i = 1, 6 do
        if self.items[i] then
            items[#items + 1] = self.items[i]
        end
    end
    return items
end

--[单位遍历物品]
function ac.unit.__index:search_items()
    if not self.items then
        return function() end
    end
    local info = {}
    local loop = 0
    for slotid = 6, 1, -1 do
        if self.items[slotid] then
            loop = loop + 1
            table.insert(info, self.items[slotid])
        end
    end
    return function()
        local obj = info[loop]
        loop = loop - 1
        return obj
    end
end

--[单位丢弃所有物品]
function ac.unit.__index:remove_items()
    for obj in self:search_items() do
        obj:drop()
    end
end

ac.item = {}
setmetatable(ac.item, {
    __index = item,
    __call = function(self, name)
        local mt = {}
        self[name] = mt
        mt.name = name
        mt.prototype = {
            __index = mt,
            __tostring = item.__tostring,
        }
        setmetatable(mt, item)
        return mt
    end,
})


return item
